/**
 * 
 */
package com.massyframework.beanskin.graphql.fetching;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

import graphql.execution.MergedField;
import graphql.language.Field;
import graphql.language.Selection;
import graphql.language.SelectionSet;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;

/**
 * {@link AbstractMergedFieldTransformer},抽象的MergedFieldTransformer变形器
 * @author huangkh
 *
 */
public abstract class AbstractMergedFieldTransformer implements MergedFieldTransformer {

	/**
	 * 
	 */
	public AbstractMergedFieldTransformer() {
		
	}

	@Override
	public MergedField transform(MergedField src, GraphQLObjectType objectType, GraphQLSchema schema) {
		if (src == null) return null;
		if (objectType == null) return null;
		
		Predicate<GraphQLFieldDefinition> filter = this.getFilter();
		List<Field> selections = this.getSelections(src, objectType, filter);
		return this.toTransform(src, selections);
	}
	
	/**
	 * GraphQL字段定义过滤器
	 * @return {@link Predicate}
	 */
	protected abstract Predicate<GraphQLFieldDefinition> getFilter();
	
	/**
	 * 执行转换
	 * @param src {@link MergedField}
	 * @param selections {@link List},选择字段集合
	 * @return {@link MergedField}
	 */
	protected MergedField toTransform(MergedField src, List<Field> selections) {
		SelectionSet ss = src.getSingleField().getSelectionSet();
		SelectionSet selected =
               SelectionSet.newSelectionSet()
                        .sourceLocation(ss.getSourceLocation())
                        .additionalData(ss.getAdditionalData())
                        .comments(ss.getComments())
                        .selections(selections)
                        .build();
		Field currentField = src.getSingleField();
		Field field = Field.newField(currentField.getName())
                .arguments(currentField.getArguments())
                .alias(currentField.getAlias())
                .directives(currentField.getDirectives())
                .selectionSet(selected)
                .build();
		return MergedField.newMergedField(field).build();
	}
	
	/**
	 * 结合{@code src}、{@code objecType}, 生成新的选择字段集合
	 * <p>新选择字段集合包括{@code objectType}的所有字段
	 * @param src {@link MergedField}
	 * @param objectType {@link GraphQLObject}
	 * @return {@link List}
	 */
	protected List<Field> getSelections(MergedField src, GraphQLObjectType objectType){
		return this.getSelections(src, objectType, null);
	}

	/**
	 * 结合{@code src}、{@code objecType}和{@code filter}生成新的选择字段集合
	 * <p>新选择字段集合包括满足{@code filter}的{@code objectType}字段
	 * @param src {@link MergedField}
	 * @param objectType {@link GraphQLObject}
	 * @param filter {@link Predicate}
	 * @return {@link List}
	 */
	@SuppressWarnings("rawtypes")
	protected List<Field> getSelections(MergedField src, GraphQLObjectType objectType, Predicate<GraphQLFieldDefinition> filter){
		List<Field> result = new ArrayList<>();
		
		Set<String> fieldNames = new HashSet<>();
		List<Selection> selections = src.getSingleField().getSelectionSet().getSelections();
		for (Selection selection : selections) {
            if (selection instanceof Field) {
                Field selectField = (Field) selection;
                fieldNames.add(selectField.getName());
                result.add(selectField);
            }
        }
		
		List<GraphQLFieldDefinition> definitions = objectType.getFieldDefinitions();
        for (GraphQLFieldDefinition definition : definitions) {
            if (!fieldNames.contains(definition.getName())) {
            	if (filter != null && !filter.test(definition)) {
            		continue;
            	}
            	
            	Field newField =
                        Field.newField().name(definition.getName()).build();
            		result.add(newField);
            }
        }
        
        
        return result;
	}
}
